﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace MetaDrive.FluentCode
{
    //http://social.microsoft.com/Forums/en-US/vsx/thread/db990169-29ac-46e6-928b-b8b8dffc1e8e
    //https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=107634&wa=wsignin1.0
    //

    public class FluentCodeVisitor
    {
        #region .: PreVisit :.

        protected virtual bool PreVisit(FluentCodeNamespace element)
        {
            return true;
        }

        protected virtual bool PreVisit(FluentCodeAttribute element)
        {
            return true;
        }

        protected virtual bool PreVisit(FluentCodeEvent element)
        {
            return true;
        }

        protected virtual bool PreVisit(FluentCodeFunction element)
        {
            return true;
        }

        protected virtual bool PreVisit(FluentCodeProperty element)
        {
            return true;
        }

        protected virtual bool PreVisit(FluentCodeParameter element)
        {
            return true;
        }

        protected virtual bool PreVisit(FluentCodeImport element)
        {
            return true;
        }

        protected virtual bool PreVisit(FluentCodeClass element)
        {
            return true;
        }

        #endregion

        #region .: Visit :.

        protected virtual void Visit(FluentCodeNamespace element)
        {            
        }

        protected virtual void Visit(FluentCodeAttribute element)
        {            
        }

        protected virtual void Visit(FluentCodeEvent element)
        {
        }

        protected virtual void Visit(FluentCodeFunction element)
        {            
        }

        protected virtual void Visit(FluentCodeProperty element)
        {            
        }

        protected virtual void Visit(FluentCodeParameter element)
        {            
        }

        protected virtual void Visit(FluentCodeImport element)
        {            
        }

        protected virtual void Visit(FluentCodeClass element)
        {
        }

        #endregion

        #region .: PostVisit :.

        protected virtual void PostVisit(FluentCodeNamespace element)
        {
        }

        protected virtual void PostVisit(FluentCodeAttribute element)
        {
        }

        protected virtual void PostVisit(FluentCodeEvent element)
        {
        }

        protected virtual void PostVisit(FluentCodeProperty element)
        {
        }

        protected virtual void PostVisit(FluentCodeParameter element)
        {
        }

        protected virtual void PostVisit(FluentCodeFunction element)
        {
        }

        protected virtual void PostVisit(FluentCodeImport element)
        {            
        }

        protected virtual void PostVisit(FluentCodeClass element)
        {
        }

        #endregion

        public virtual void Visit(IEnumerable<IFluentCodeElement> elements)
        {
            foreach (var item in elements)
                Visit((IFluentCodeElement)item);
        }

        public virtual void Visit(IFluentCodeElement element)
        {            
            bool visit = (bool)Invoke("PreVisit", element.GetType(), element);

            if (visit)
            {
                Invoke("Visit", element.GetType(), element);

                foreach (var childElement in element.Query.Children)
                {
                    Visit(childElement);
                }            
            }

            Invoke("PostVisit", element.GetType(), element);                        
        }
                 
        protected object Invoke(string methodName, Type parameterType, object parameter)
        {
            MethodInfo methodInfo = this.GetType().GetMethod(
                                        methodName,
                                        BindingFlags.NonPublic | BindingFlags.Instance,
                                        Type.DefaultBinder,
                                        new Type[] { parameterType },
                                        new ParameterModifier[0]);

            return methodInfo.Invoke(this, BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.InvokeMethod, null, new object[] { parameter }, null);
        }
    }
}
